#include "stdafx.h"
#include  "types.h"

#include "cpuid.h"
//---------------------------------------------------------------------------

/* Symbolic constants for feature flags in CPUID standard feature flags */

#define CPUID_STD_FPU          0x00000001
#define CPUID_STD_VME          0x00000002
#define CPUID_STD_DEBUGEXT     0x00000004
#define CPUID_STD_4MPAGE       0x00000008
#define CPUID_STD_TSC          0x00000010
#define CPUID_STD_MSR          0x00000020
#define CPUID_STD_PAE          0x00000040
#define CPUID_STD_MCHKXCP      0x00000080
#define CPUID_STD_CMPXCHG8B    0x00000100
#define CPUID_STD_APIC         0x00000200
#define CPUID_STD_SYSENTER     0x00000800
#define CPUID_STD_MTRR         0x00001000
#define CPUID_STD_GPE          0x00002000
#define CPUID_STD_MCHKARCH     0x00004000
#define CPUID_STD_CMOV         0x00008000
#define CPUID_STD_PAT          0x00010000
#define CPUID_STD_PSE36        0x00020000
#define CPUID_STD_MMX          0x00800000
#define CPUID_STD_FXSAVE       0x01000000
#define CPUID_STD_SSE          0x02000000
#define CPUID_STD_SSE2         0x04000000

/* Symbolic constants for feature flags in CPUID extended feature flags */

#define CPUID_EXT_3DNOW        0x80000000
#define CPUID_EXT_AMD_3DNOWEXT 0x40000000
#define CPUID_EXT_AMD_MMXEXT   0x00400000

/* Define known vendor strings here */
const char vendorAMD  [13] = "AuthenticAMD";
const char vendorIntel[13] = "GenuineIntel";
char  vendor[13] = "UnknownVendr";

DWORD GetCpuId(void)
{
    DWORD result     = 0;
    DWORD signature  = 0;

#ifdef WIN32

    /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Step 1: Check if processor has CPUID support. Processor faults
    ;; with an illegal instruction exception if the instruction is not
    ;; supported. This step catches the exception and immediately returns
    ;; with feature string bits with all 0s, if the exception occurs.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
    try {
      _asm {
        xor eax, eax
        xor ebx, ebx
        xor ecx, ecx
        xor edx, edx
        cpuid
      }
    }
    catch(...) {
      return 0;
    }
    result |= FEATURE_CPUID;

    _asm {
      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 2: Check if CPUID supports function 1 (signature/std features)
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      xor     eax, eax                      // CPUID function #0
      cpuid                                 // largest std func/vendor string
      mov     dword ptr [vendor], ebx       // save
      mov     dword ptr [vendor+4], edx     //  vendor
      mov     dword ptr [vendor+8], ecx     //   string
      test    eax, eax                      // largest standard function==0?
      jz      $no_standard_features         // yes, no standard features func
      or      [result], FEATURE_STD_FEATURES// does have standard features

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 3: Get standard feature flags and signature
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      mov     eax, 1                        // CPUID function #1
      cpuid                                 // get signature/std feature flgs
      mov     [signature], eax              // save processor signature

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 4: Extract desired features from standard feature flags
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      // Check for time stamp counter support

      mov     ecx, CPUID_STD_TSC            // bit 4 indicates TSC support
      and     ecx, edx                      // supports TSC ? CPUID_STD_TSC:0
      neg     ecx                           // supports TSC ? CY : NC
      sbb     ecx, ecx                      // supports TSC ? 0xffffffff:0
      and     ecx, FEATURE_TSC              // supports TSC ? FEATURE_TSC:0
      or      [result], ecx                 // merge into feature flags

      // Check for MMX support

      mov     ecx, CPUID_STD_MMX            // bit 23 indicates MMX support
      and     ecx, edx                      // supports MMX ? CPUID_STD_MMX:0
      neg     ecx                           // supports MMX ? CY : NC
      sbb     ecx, ecx                      // supports MMX ? 0xffffffff:0
      and     ecx, FEATURE_MMX              // supports MMX ? FEATURE_MMX:0
      or      [result], ecx                 // merge into feature flags

      // Check for CMOV support

      mov     ecx, CPUID_STD_CMOV           // bit 15 indicates CMOV support
      and     ecx, edx                      // supports CMOV?CPUID_STD_CMOV:0
      neg     ecx                           // supports CMOV ? CY : NC
      sbb     ecx, ecx                      // supports CMOV ? 0xffffffff:0
      and     ecx, FEATURE_CMOV             // supports CMOV ? FEATURE_CMOV:0
      or      [result], ecx                 // merge into feature flags
         
      // Check support for P6-style MTRRs

      mov     ecx, CPUID_STD_MTRR           // bit 12 indicates MTRR support
      and     ecx, edx                      // supports MTRR?CPUID_STD_MTRR:0
      neg     ecx                           // supports MTRR ? CY : NC
      sbb     ecx, ecx                      // supports MTRR ? 0xffffffff:0
      and     ecx, FEATURE_P6_MTRR          // supports MTRR ? FEATURE_MTRR:0
      or      [result], ecx                 // merge into feature flags

      // Check for initial SSE support. There can still be partial SSE
      // support. Step 9 will check for partial support.

      mov     ecx, CPUID_STD_SSE            // bit 25 indicates SSE support
      and     ecx, edx                      // supports SSE ? CPUID_STD_SSE:0
      neg     ecx                           // supports SSE ? CY : NC
      sbb     ecx, ecx                      // supports SSE ? 0xffffffff:0
      and     ecx, (FEATURE_MMXEXT+FEATURE_SSEFP) // supports SSE ?
                                            // FEATURE_MMXEXT+FEATURE_SSEFP:0
      or      [result], ecx                 // merge into feature flags

      // Check for initial SSE2 support. There can still be partial SSE
      mov     ecx, CPUID_STD_SSE2           // bit 26 indicates SSE2 support
      and     ecx, edx                      // supports SSE2?CPUID_STD_SSE2:0
      neg     ecx                           // supports SSE2 ? CY : NC
      sbb     ecx, ecx                      // supports SSE2 ? 0xffffffff:0
      and     ecx, FEATURE_SSE2             // supports SSE2 ? FEATURE_SSE2:0
      or      [result], ecx                 // merge into feature flags

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 5: Check for CPUID extended functions
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      mov     eax, 0x80000000               // extended function 0x80000000
      cpuid                                 // largest extended function
      cmp     eax, 0x80000000               // no function > 0x80000000 ?
      jbe     $no_extended_features         // yes, no extended feature flags
      or      [result], FEATURE_EXT_FEATURES// does have ext. feature flags

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 6: Get extended feature flags
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      mov     eax, 0x80000001               // CPUID ext. function 0x80000001
      cpuid                                 // EDX = extended feature flags

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 7: Extract vendor independent features from extended flags
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      // Check for 3DNow! support (vendor independent)

      mov     ecx, CPUID_EXT_3DNOW          // bit 31 indicates 3DNow! supprt
      and     ecx, edx                      // supp 3DNow! ?CPUID_EXT_3DNOW:0
      neg     ecx                           // supports 3DNow! ? CY : NC
      sbb     ecx, ecx                      // supports 3DNow! ? 0xffffffff:0
      and     ecx, FEATURE_3DNOW            // support 3DNow!?FEATURE_3DNOW:0
      or      [result], ecx                 // merge into feature flags

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 8: Determine CPU vendor
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      lea     esi, vendorAMD                // AMD's vendor string
      lea     edi, vendor                   // this CPU's vendor string
      mov     ecx, 12                       // strings are 12 characters
      cld                                   // compare lowest to highest
      repe    cmpsb                         // current vendor str == AMD's ?
      jnz     $not_AMD                      // no, CPU vendor is not AMD

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 9: Check AMD specific extended features
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      mov     ecx, CPUID_EXT_AMD_3DNOWEXT   // bit 30 indicates 3DNow! ext.
      and     ecx, edx                      // 3DNow! ext?
      neg     ecx                           // 3DNow! ext ? CY : NC
      sbb     ecx, ecx                      // 3DNow! ext ? 0xffffffff : 0
      and     ecx, FEATURE_3DNOWEXT         // 3DNow! ext?FEATURE_3DNOWEXT:0
      or      [result], ecx                 // merge into feature flags

      test    [result], FEATURE_MMXEXT      // determined SSE MMX support?
      jnz     $has_mmxext                   // yes, don't need to check again

      // Check support for AMD's multimedia instruction set additions

      mov     ecx, CPUID_EXT_AMD_MMXEXT     // bit 22 indicates MMX extension
      and     ecx, edx                      // MMX ext?CPUID_EXT_AMD_MMXEXT:0
      neg     ecx                           // MMX ext? CY : NC
      sbb     ecx, ecx                      // MMX ext? 0xffffffff : 0
      and     ecx, FEATURE_MMXEXT           // MMX ext ? FEATURE_MMXEXT:0
      or      [result], ecx                 // merge into feature flags

      $has_mmxext:

      /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Step 10: Check AMD-specific features not reported by CPUID
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

      // Check support for AMD-K6 processor-style MTRRs

      mov     eax, [signature]       // get processor signature
      and     eax, 0xFFF             // extract family/model/stepping
      cmp     eax, 0x588             // CPU < AMD-K6-2/CXT ? CY : NC
      sbb     edx, edx               // CPU < AMD-K6-2/CXT ? 0xffffffff:0
      not     edx                    // CPU < AMD-K6-2/CXT ? 0:0xffffffff
      cmp     eax, 0x600             // CPU < AMD Athlon ? CY : NC
      sbb     ecx, ecx               // CPU < AMD-K6 ? 0xffffffff:0
      and     ecx, edx               // (CPU>=AMD-K6-2/CXT)&&
                                        // (CPU<AMD Athlon) ? 0xffffffff:0
      and     ecx, FEATURE_K6_MTRR   // (CPU>=AMD-K6-2/CXT)&&
                                        // (CPU<AMD Athlon) ? FEATURE_K6_MTRR:0
      or      [result], ecx          // merge into feature flags

      jmp     $all_done              // desired features determined

      $not_AMD:

      /* Extract features specific to non AMD CPUs */

      $no_extended_features:
      $no_standard_features:
      $all_done:
    }

    /* The FP part of SSE introduces a new architectural state and therefore
      requires support from the operating system. So even if CPUID indicates
      support for SSE FP, the application might not be able to use it. If
      CPUID indicates support for SSE FP, check here whether it is also
      supported by the OS, and turn off the SSE FP feature bit if there
      is no OS support for SSE FP.

      Operating systems that do not support SSE FP return an illegal
      instruction exception if execution of an SSE FP instruction is performed.
      Here, a sample SSE FP instruction is executed, and is checked for an
      exception using the (non-standard) __try/__except mechanism
      of Microsoft Visual C.
    */

    if (result & FEATURE_SSEFP) {
      try {
        #ifdef __BORLANDC__
        _asm {
          db 0x0f
          db 0x56
          db 0xC0    // orps xmm0, xmm0
        }
        #else
        _asm {
          __emit(0x0f)
          __emit(0x56)
          __emit(0xC0)    // orps xmm0, xmm0
        }
        #endif
        return result;
      }
      catch(...) {
        return result & ~FEATURE_SSEFP;
      }
   } else {
     return result;
   }
#else  // WIN32
   return result;
#endif // WIN32
}

int getCPUVendor(void)
{
  if (!strcmpi(vendor, vendorIntel)) return CPU_VENDOR_INTEL;
  if (!strcmpi(vendor, vendorAMD  )) return CPU_VENDOR_AMD;
  return CPU_VENDOR_UNKNOWN;
}


/*
static const char *YNO(int CC)
{
    return (CC != 0) ? "Y" : "N";
}

static const char *CBX(int CC)
{
    return (CC != 0) ? "[X]" : "[ ]";
}

void PrintCpuInfo(void)
{
    dword cap = GetCpuId();

    Print  ("CPU vendor is %s.\n", vendor, cap);

    Print  ("CPU features ( %08x ) :\n", cap);
    Print  ("- MMX   : %s\n", CBX(cap & FEATURE_MMX));
    Print  ("- 3DNOW : %s\n", CBX(cap & FEATURE_3DNOW));
    Print  ("- SSE   : %s\n", CBX(cap & FEATURE_SSEFP));

    Print_d("--- CPU supports ---\n");
    Print_d("CPUID       : %s\n", YNO(cap & FEATURE_CPUID));
    Print_d("CPUID STD   : %s\n", YNO(cap & FEATURE_STD_FEATURES));
    Print_d("CPUID EXT   : %s\n", YNO(cap & FEATURE_EXT_FEATURES));
    Print_d("TSC         : %s\n", YNO(cap & FEATURE_TSC));
    Print_d("CMOV        : %s\n", YNO(cap & FEATURE_CMOV));
    Print_d("MMX         : %s\n", YNO(cap & FEATURE_MMX));
    Print_d("3DNOW       : %s\n", YNO(cap & FEATURE_3DNOW));
    Print_d("3DNOW_EXT   : %s\n", YNO(cap & FEATURE_3DNOWEXT));
    Print_d("AMD-K6-MTRR : %s\n", YNO(cap & FEATURE_K6_MTRR));
    Print_d("P6-MTRR     : %s\n", YNO(cap & FEATURE_P6_MTRR));
    Print_d("SSE MMX     : %s\n", YNO(cap & FEATURE_MMXEXT));
    Print_d("SSE FPU     : %s\n", YNO(cap & FEATURE_SSEFP));
    Print_d("--------------------\n");
}
*/

